package com.windbell.rest.services

import com.uniquesoft.gwt.shared.validation.ValidationException
import com.uniquesoft.uidl.validation.Validates
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

class RestUtil {
	def static <T> callWithResult(()=>T value){
	    //exception handled by ValidationExceptionHandler
		new RestResult("I001","success",value.apply())
	}
	def static <T> call(()=>void value){
	    //exception handled by ValidationExceptionHandler
		value.apply
		new RestResult("I001","success",null)
	}
		
	def static Assert(Boolean assertion, String code, String message){
		Validates::Assert(assertion,code,message)
	}

	def static Assert(Boolean assertion, String code, String message, ()=>void action){
		if(!assertion){
			action.apply
		}
		Validates::Assert(assertion,code,message)
	}
}

class RestResult<T>{
	String code
	String message
	T value
	new (String code,String message,T value){
		this.code = code
		this.message = message
		this.value = value
	}

	def getCode(){
		code
	}
	def getMessage(){
		message
	}
	def getValue(){
		value
	}
}

/**
 * uncaught validation errors handled here
 */
@ControllerAdvice
class RestExceptionHandler extends ResponseEntityExceptionHandler{
    
    @ExceptionHandler(Throwable)
    def ResponseEntity<RestResult> onValidationException(Throwable e){
    	e.printStackTrace
        var status = HttpStatus.OK;
        if(e instanceof ValidationException){
            if (e.errors.head?.keys.head == "UNAUTHORIZED"){
                status = HttpStatus.UNAUTHORIZED
            }
            val result = new RestResult(e.errors.map[it.keys].flatten.join(','),e.errors.map[it.message].join(','),null)
            return new ResponseEntity(result, status);
        }else{
            val result = new RestResult("__INTERNAL_ERROR__",e.message,null)
            return new ResponseEntity(result, status);
        }
    }
    
}

@Service
@Transactional
class TransactionalRunner {
	def <T> T call (()=>T f){
		return f.apply()
	}
	def void exec(()=>void p){
		p.apply()
	}
	
}
